package personal.parser;

import personal.util.Map2D;
import personal.util.Pair;

import java.util.*;

public class SimpleStatus extends LinkedHashSet<SimpleItem> implements ParserFA.Status {
    @Override
    public Set<Grammar.Symbol> getCurrents() {
        Set<Grammar.Symbol> ret = new HashSet<>();
        for (SimpleItem item : this) {
            try {
                ret.add(item.getCurrent());
            } catch (IndexOutOfBoundsException ignored) { // 是可归约项
            }
        }
        return ret;
    }

    /**
     * 对自己求闭包
     *
     * @return 自己
     */
    public SimpleStatus toClosure(Grammar grammar) {
        Queue<SimpleItem> qeBuffer = new LinkedList<>(this);
        while (!qeBuffer.isEmpty()) { // BF 算法求闭包
            SimpleItem itemU = qeBuffer.poll();
            for (SimpleItem itemV : this.transfer(grammar, itemU)) {
                if (this.add(itemV)) qeBuffer.add(itemV); // 放入新的 itemV
            }
        }
        // 合并 lookahead
        Map2D<Grammar.Production, Integer, SimpleItem> bucket = new Map2D<>(); // 用产生式、当前位置作 key
        for (SimpleItem item : this) {
            SimpleItem preItem = bucket.putIfAbsent(item.production(), item.point(), item);
            if (preItem != null) preItem.merged(item);
        }
        this.clear();
        this.addAll(bucket.values());
        return this;
    }

    /**
     * @param grammar 指定文法
     * @param itemU   指定 item
     * @return 指定文法中，由指定 item 推导出的 item 集合
     */
    private List<SimpleItem> transfer(Grammar grammar, SimpleItem itemU) {
        List<SimpleItem> ret = new ArrayList<>();
        try {
            Grammar.NonTerminal current = (Grammar.NonTerminal) itemU.getCurrent(); // 当前符号
            List<Grammar.Production> productions = grammar.getProductions().stream().filter(i -> i.leftSide().equals(current)).toList(); // 取出文法中，左部是当前符号的产生式
            for (Grammar.Production production : productions) { // 遍历左部是当前符号的产生式
                // 生成 lookahead
                Set<Grammar.Terminal> lookahead = new HashSet<>();
                for (Grammar.Terminal terminal : itemU.lookahead()) { // 遍历 itemU 的 lookahead 集合，
                    List<Grammar.Symbol> buffer = itemU.getFollowSymbols();
                    buffer.add(terminal);
                    lookahead.addAll(grammar.fist(buffer.toArray(new Grammar.Symbol[0]))); // 将对应的 first 集合并到 lookahead
                }
                ret.add(new SimpleItem(production, lookahead)); // 放入
            }
        } catch (IndexOutOfBoundsException ignored) { // 到达产生式结尾
        } catch (ClassCastException ignored) { // 当前符号不是非终结符
        }
        return ret;
    }


    @Override
    public String toString() {
        StringBuilder ret = new StringBuilder();
        for (SimpleItem item : this) ret.append(item).append("\n");
        return ret.toString();
    }

    @Override
    public Set<ParserFA.Item> Items() {
        return new HashSet<>(this);
    }

    @Override
    public boolean isReducible() {
        if (!this.initialized) throw new UnsupportedOperationException("节点状态未初始化");
        return this.isReducible;
    }

    @Override
    public boolean haveSRConflict() {
        if (!this.initialized) throw new UnsupportedOperationException("节点状态未初始化");
        return this.haveSRConflict;
    }

    @Override
    public boolean haveRRConflict() {
        if (!this.initialized) throw new UnsupportedOperationException("节点状态未初始化");
        return this.haveRRConflict;
    }

    private boolean isReducible, haveSRConflict, haveRRConflict, initialized = false;

    /**
     * 初始化相关状态：是否可归约、是否具有移入-归约冲突、是否具有归约-归约冲突
     */
    public void initTag() {
        if (initialized) throw new UnknownError("禁止反复初始化节点的 tag");
        // 初始化 bucket
        Map<Grammar.Terminal, Pair<Integer, Integer>> cntMap = new HashMap<>(); // <lookahead, <reduceCnt, shiftCnt>>
        for (ParserFA.Item item : this) {
            boolean reducible = item.getCurrentOrNull() == null; // 在结尾，是可归约的
            for (Grammar.Terminal lookahead : item.lookahead()) { // 遍历各个 lookahead
                cntMap.putIfAbsent(lookahead, new Pair<>(0, 0));
                Pair<Integer, Integer> cnt = cntMap.get(lookahead);
                if (reducible) {
                    cnt.setFirst(cnt.getFirst() + 1);
                } else {
                    cnt.setSecond(cnt.getSecond() + 1);
                }
            }
        }
        // 处理
        for (Map.Entry<Grammar.Terminal, Pair<Integer, Integer>> entry : cntMap.entrySet()) {
            int reduceCnt = entry.getValue().getFirst(), shiftCnt = entry.getValue().getSecond();
            if (reduceCnt == 0) continue; // 不可归约，那么跳过
            this.isReducible = true; // 是可归约的
            if (reduceCnt > 1) this.haveRRConflict = true; // 多个归约项
            if (shiftCnt > 0) this.haveSRConflict = true; // 还可移入
        }
        this.initialized = true;

    }


}
